#pragma once
#include "Types/AtomicType.h"
#include "Types/AbstractType.h"
#include "TermVariable.h"
#include "ApplicationTerm.h"
#include "AbstractTerm.h"
#include "TermAbstractedType.h"

#include <vector>
#include <map>
#include <algorithm>
using namespace std;

class TokenSetXception : public Token::exception{
public:
	TokenSetXception(string err):Token::exception(string("At TokenSetXception  ->\r\n") + err){};
	const TokenSetXception& outOfBound( ){
					errorMsg += "Out of Bound Exception";
					return *(this);	
				};
	const TokenSetXception& emptySet( ){
					errorMsg += "Illegal Operation on Empty TokenSet Exception";
					return *(this);	
				};
};


class TokenSet{

public:
	typedef TokenSet* pointer;

protected:
	size_t vecSize;

public:
	typedef vector<Token::pointer> vecTokenPtr;
	typedef map<size_t , vecTokenPtr>::const_iterator PartitionIterator;
	typedef TokenSetXception exception;


protected:
	void copyMap(const TokenSet&);
	void destroyMap();
	map<size_t , vecTokenPtr> Partition;

public:
	TokenSet();
	TokenSet(const TokenSet&);
	virtual ~TokenSet();
	
public:
	 Token::const_reference getRandToken() const;
	 PartitionIterator beginMinimumComplexity(size_t) const;

public:
	virtual bool find(Token::const_reference) const; 
	virtual Token::const_reference operator[](size_t index) const;
	virtual bool insert(Token::const_reference);
	virtual const TokenSet& operator=(const TokenSet&);
	virtual size_t size() const;
	virtual string toString()const;

	};



class TermSet : public TokenSet{
 
protected:
	TokenSet   Types;
    
public:
	size_t TySize()const{return Types.size();};
	size_t TeSize()const{return size();};
  
public:
	TermSet();
	TermSet(const TermSet&);
	virtual ~TermSet();

public:
	bool hasType(Type::const_reference) const;
	bool hasTerm(Term::const_reference) const;

public:
	bool insert(Token::const_reference);
	string toString()const;
	bool find(Term::const_reference Te) const;

public:
	Term::const_reference   getRandTerm() const;	 
	Type::const_reference   getRandType() const;
	Term::const_reference   getRandTermOfType(Type::const_reference) const;
	Term::const_reference   getRandFunctionInDomain(Type::const_reference) const;
	Term::const_reference   getRandFunctionToDomain(Type::const_reference) const; 
};









